﻿using System;
using System.Collections;

namespace Lesson2_Stack
{
    class Test { }
      class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("栈");
            #region 知识点一 Stack的本质
            //Stack(栈)是一个C#为我们封装好的类
            //他的本质也是object[]数组，只是封装了特殊的存储规则

            //Stack是栈存储容器，栈是一种先进后出的数据结构
            //先存入的数据后获取，后存入的数据先获取   （=类似于子弹弹匣）
            //栈是先进后出
            #endregion

            #region 知识点二 申明
            //需要引用命名空间 using System.Collections;
            Stack stack = new Stack();
            #endregion

            #region 知识点三 增取查改

            #region 增
            //压栈     子弹压弹匣同理 只能一个一个得从栈顶压进去
            stack.Push(1);
            stack.Push("123");
            stack.Push(true);
            stack.Push(1.2f);
            stack.Push(new Test());

            #endregion

            #region 取
            //栈中不存在删除的概念
            //只有取的概念
            //弹栈  后放进去的先弹出来 弹匣同理 只能从栈顶拿出来
            object v = stack.Pop();
            Console.WriteLine(v);//取出 最后那个元素——类 后进先出

            v = stack.Pop();
            Console.WriteLine(v);//取出 最后那个元素——1.2f
            #endregion

            #region 查
            //1.栈无法查看指定位置的元素 栈的特点只能是压栈和弹栈
            //  如果要查的话，只能查看栈顶的内容 
            v = stack.Peek();//这个只是用来查看栈顶的内容，并不是弹出来
                             //如果没有在后续做弹栈的动作的话，他就一直会是那个元素
            Console.WriteLine(v);
            v= stack.Pop();
            Console.WriteLine(v);
            //2.查看元素是否存在于栈中
            //stack.Contains("123");   查找元素是否存于栈中并返回bool值
            if (stack.Contains("123"))
            {
                Console.WriteLine("字符串123存在于栈中");
            }

            #endregion

            #region 改
            //栈无法改变其中的元素 只能压(存)和弹(取)
            //是在要改 只有清空
            stack.Clear();//清空栈中的元素
            Console.WriteLine("清空栈中的元素 长度为"+stack.Count);
            stack.Push("1");
            stack.Push(2); 
            stack.Push("哈哈哈哈");
            #endregion

            #endregion

            #region 知识点四 遍历  
            //1.栈没有办法通过for循环遍历的，因为他只能查看栈顶的元素，没有索引器来进行查看
            //长度  
            Console.WriteLine(stack.Count);
            //2.用foreach遍历
            //而且遍历出来的顺序 也是从栈顶到栈底
            Console.WriteLine("********foreach遍历栈**********");
            foreach (object item in stack)
            {
                Console.WriteLine(item);
            }

            //3.还有一种遍历方式 
            //将栈转换为object数组
            //遍历出来的顺序 也是从栈顶到栈底
            Console.WriteLine("********栈转数组for遍历**********");
            object[] array = stack.ToArray();
            for (int i = 0; i < array.Length; i++)//现在使用for循环 是因为已经把栈转换为数组了 下标的顺序对应着栈顶到栈底
            {
                Console.WriteLine(array[i]);
            }
            Console.WriteLine("********循环弹栈**********");
            Console.WriteLine(stack.Count);
            //4.循环弹栈 上面的遍历模式只是用来查看栈里面有什么元素，并不会改变栈的内容 下面的循环弹栈是直接取出来的 直接影响栈的内容
            while (stack.Count>0)
            {
                Console.WriteLine(stack.Pop());//弹栈
            }
            //已经把栈里面的元素都弹出来了，再弹的话就会提示为空

            #endregion

            #region 知识点五 装箱拆箱
            //由于用万物之父object来存储苏局，自然就存在装箱拆箱。
            //当往其中进行值类型存储时就是在装箱
            //当将值类型对象取出来转换使用时，就存在拆箱。
            stack.Push("hhh");
            string s =  stack.Pop().ToString();//拆箱
            Console.WriteLine(s.GetType());

            stack.Push(112);//装箱
            Console.WriteLine(stack.GetType());//查看
            #endregion
            #region 练习题 
            //写一个方法计算任意一个数的二进制使用栈结构方式存储，之后打印出来
            Binary(20);

            void Binary(int num)
            {
                Stack binary = new Stack();//存储二进制数
                do//二进制转换 短除法
                {
                    binary.Push(num % 2);
                    num /= 2;//如果除到小数的话，int会向下取整
                } while (num != 0);
                //打印结果
                PrintStack(binary);
            }

            void PrintStack(Stack stack)
            {
                foreach (object item in stack)
                {
                    Console.Write(item);
                }
            }
            #endregion
        }

    }
}
